home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-05-30 | 19.6 KB | 630 lines | [TEXT/ALFA] |
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Grayscale Image
- *
- * The image is represented as a Pixmap, i.e. a matrix of pixels
- * each of them specifies the gray level at a particular point
- *
- * The header file defines arithmetical and all other operations
- * permitted on the grayscale image
- *
- * $Id: image.h,v 1.16 1995/03/17 18:00:57 oleg Exp oleg $
- *
- ************************************************************************
- */
-
- #ifndef __GNUC__
- #pragma once
- #endif
- #ifndef _image_h
- #define _image_h
-
- #ifdef __GNUC__
- #pragma interface
- #endif
-
- #include "myenv.h"
- #include "std.h"
-
- typedef unsigned short GRAY; // Pixel type
- typedef signed short GRAY_SIGNED; // Pixel type, signed
- const int GRAY_MAXBIT = 8*sizeof(GRAY); // Max no of bits per pixel
- const int GRAY_MAXVAL = ((1<<GRAY_MAXBIT)-1);
- typedef unsigned int card; // Better be short, but compilers
- // seem to like 'int' better
-
- class Rectangle;
- class rowcol;
- class Extrema;
- class EndianIn;
- class IMAGE;
- class FilterIt;
-
- // A class to do a specific operation on
- // every pixel element regardless of its
- // position. The matrix is traversed
- // row-by-row
- class PixelPrimAction
- {
- friend class IMAGE;
- virtual void operation(GRAY& pixel) = 0;
- // Those are'n implemented; but since they're
- // private, it forbids the assignement
- // PixelPrimAction(const PixelPrimAction&);
- PixelPrimAction& operator= (const PixelPrimAction&);
- };
-
- // A class to do a specific operation on
- // every matrix element as the matrix
- // is efficiently traversed (row-by-row)
- class PixelAction
- {
- friend class IMAGE;
- virtual void operation(GRAY& pixel) = 0;
-
- protected:
- card nrows, ncols; // These are set up by IMAGE::apply() function
- // and available to PixelAction::operation()
- card row, col; // position of the element being passed to
- // 'operator()'
-
-
- private: // Those are'n implemented; but since they're
- // private, it forbids the assignement
- // PixelAction(const PixelAction&);
- PixelAction& operator= (const PixelAction&);
- };
-
- // Lazy image constructor
- // It's highly recommended a function never
- // return IMAGE itself. Use LazyImage
- // instead
- class LazyImage
- {
- friend class IMAGE;
- virtual void fill_in(IMAGE& im) const = 0;
-
- LazyImage(const LazyImage&); // Don't implement to forbid assignment
- LazyImage& operator = (const LazyImage&);
-
- protected:
- card nrows, ncols, depth;
- public:
- LazyImage(const card _nrows, const card _ncols, const card _depth)
- : nrows(_nrows), ncols(_ncols), depth(_depth) {}
- };
-
- class IMAGE
- {
- friend class Rectangle;
- friend class Extrema;
- friend class FilterIt;
-
- private: // Private part
- int valid_code; // Validation code
- enum { IMAGE_val_code = 577767 }; // Image validation code
- card ncols; // Image width in pixels
- card nrows; // Image height in pixels
- unsigned long int npixels; // Total no of pixels in the image
- char * name; // Image name
- card bits_per_pixel; // Image depth
- GRAY ** scanrows; // scanrows[i] = &pixels[i,0]
- GRAY * pixels; // pixels[i,j] is the
- // pixel value at point (i,j)
- // Pixels are ordered in rows
-
- void allocate(const card nrows, const card ncols, const card depth);
-
- void _expand(const IMAGE& prototype); // Expand the prototype twice
- void _shrink(const IMAGE& prototype); // Shrink the prototype twice
-
- // The following functions create an
- // image from a particular file
- // format. They call allocate()
- // to allocate the image data and
- // then read the data. The functions
- // are private to an IMAGE(file_name)
- // constructor and should be used
- // only by it
- void read_xwd(EndianIn& file, const bool verbose); // read an X Window dump
- void read_pgm(EndianIn& file, const bool verbose);// read a Portable GrayMap
- void read_tiff(EndianIn& file, const bool verbose); // read TIFF
-
- public: // Public interface
-
- // Constructors and destructors
- // Make a blank image
- IMAGE(const card nrows, const card ncols, const card depth);
- IMAGE(const IMAGE& image); // Make a new blank image like
- // Read an image from the file
- // Attempts to detect the file format
- IMAGE(const char * file_name,const bool print_header_info = false);
- IMAGE(const Rectangle& area); // Make an image from a rect area
- // of another image
-
- // Construct an image applying a spec
- // operation to the prototype
- enum IMAGE_CREATORS_1op { Expand, Shrink };
- IMAGE(const IMAGE_CREATORS_1op op, const IMAGE& prototype);
-
- IMAGE(const LazyImage& lazy_constructor);//Make an image using given recipe
-
- ~IMAGE(void);
-
- void is_valid(void) const
- { assure(valid_code == IMAGE_val_code,"Invalid image"); }
-
- // Status
- card q_nrows() const { return nrows; }
- card q_ncols() const { return ncols; }
- card q_depth() const { return bits_per_pixel; }
- unsigned long int q_npixels() const { return npixels; }
- const char * q_name() const { return name; }
- void set_name(const char * new_name);
-
- // Individual pixel manipulations
- const GRAY operator () (const card row, const card col) const;
- GRAY& operator () (const card row, const card col);
- const GRAY operator () (const rowcol pos) const;
- GRAY& operator () (const rowcol pos);
- void sure_within(const rowcol pos) const; // Make sure the pos
- // is within the image
-
- // Row Column, Square operations
-
- // Image-scalar operations
-
- // Find out if the predicate
- // "(signed)pixel op val" is true for ALL
- // pixels of the image?
- bool operator == (const int val) const; // ? (signed)pixels == val
- bool operator != (const int val) const; // ? (signed)pixels != val
- bool operator < (const int val) const; // ? (signed)pixels < val
- bool operator <= (const int val) const; // ? (signed)pixels <= val
- bool operator > (const int val) const; // ? (signed)pixels > val
- bool operator >= (const int val) const; // ? (signed)pixels >= val
-
- // Modify every element of the
- // image according to the operation
- IMAGE& operator = (const int val); // Assignment to all the pixels
- IMAGE& operator -= (const int val); // Diminish the brightness
- IMAGE& operator += (const int val); // Increase the brightness
- IMAGE& operator *= (const int val);
- IMAGE& operator |= (const int val); // OR
- IMAGE& operator &= (const int val); // AND
- IMAGE& operator ^= (const int val); // XOR
- IMAGE& operator <<= (const int val); // Shift all the pixels
- IMAGE& operator >>= (const int val); // Shift all the pixels
-
-
- // Single image operations
- IMAGE& clear(void); // Clear the image
- IMAGE& invert(void); // Invert the image
- IMAGE& abs(void); // pixel = |(signed)pixel|
- IMAGE& clip_to_intensity_range(void); // Clip pixel values to
- // [0,1<<bits_per_pixel-1]
- IMAGE& normalize_for_display(void); // Normalize pixel values to be
- // in range 0..1<<bits_per_pixel-1
- IMAGE& equalize(const int no_grays); // Perform the histogram equalization
-
- IMAGE& apply(PixelPrimAction& action);// Apply a user-defined action
- IMAGE& apply(PixelAction& action); // to each pixel
-
- // Estimate the norm of the (signed) image
- double norm_1(void) const; // SUM{ |(signed)pixel[i,j]| }
- double norm_2_sqr(void) const; // SUM{ (signed)pixel[i,j]^2 }
- int norm_inf(void) const; // MAX{ |(signed)pixel[i,j]| }
-
- // Two images operations
- IMAGE& operator = (const IMAGE& source); // Assignment
- // Assign another to '*this' resizing
- IMAGE& coerce(const IMAGE& another); // '*this' as necessary to fit (by
- // not necessarily int factor!)
- friend bool operator == (const IMAGE& im1, const IMAGE& im2);
- friend void compare(const IMAGE& im1, const IMAGE& im2,
- const char * title);
- friend inline void are_compatible(const IMAGE& im1, const IMAGE& im2);
-
- // Arithmetics
- IMAGE& operator += (const IMAGE& im);
- IMAGE& operator -= (const IMAGE& im);
- //?? IMAGE& add(IMAGE& target, const int scalar,const IMAGE& source);
- // Shift the source to 'pos'
- // clip if necessary
- // multiply by scalar
- // and add
- // IMAGE& shift_clip_add(rowcol pos, const int scalar, const IMAGE& source);
-
- // Logic
- IMAGE& operator |= (const IMAGE& im);
- IMAGE& operator &= (const IMAGE& im);
- IMAGE& operator ^= (const IMAGE& im);
-
- // Scalar product
- friend double operator * (const IMAGE& im1, const IMAGE& im2);
-
- // Estimate the norm of the difference
- // between two (signed) image
- // SUM{ |(signed)pixel[i,j]| }
- friend double norm_1(const IMAGE& im1, const IMAGE& im2);
- // SUM{ (signed)pixel[i,j]^2 }
- friend double norm_2_sqr(const IMAGE& im1, const IMAGE& im2);
- // MAX{ |(signed)pixel[i,j]| }
- friend int norm_inf(const IMAGE& im1, const IMAGE& im2);
-
-
- // I/O: write, read, display, print info
- // Write to a file
- // "| command name" is OK as a file
- // name
- void write(const char * file_name,const char * title = "") const
- { write_xwd(file_name,title); }
- // Write an X window dump
- void write_xwd(const char * file_name,const char * title = "") const;
- // Write a Portable GrayMap
- void write_pgm(const char * file_name,const char * title = "") const;
- // Write a TIFF file
- void write_tiff(const char * file_name,const char * title = "") const;
- void display(const char * title) const;
- void info(void) const; // Print the info about the image
- void print(const char * title) const; // Print the image as a table
-
- // Clip a square area of the image
- Rectangle square_of (const card size, const rowcol pos);
- // Clip a rectangular area of the image
- // specified by the coordinates of
- // the upper-left and lower-right corners
- Rectangle rectangle (const rowcol uppleft, const rowcol lowright);
- };
-
- /*
- *------------------------------------------------------------------------
- * Position specification and operations on it
- */
-
- class rowcol { // Specifying the row/col position
- friend class Extrema;
-
- protected:
- short int row_val; // both row, col start from 0
- short int col_val;
-
- public:
- // Constructors
- rowcol(const int row, const int col) : row_val(row), col_val(col) {}
- //?? rowcol(void) : row_val(-1), col_val(-1) {}
-
- card row(void) const { return row_val; }
- card col(void) const { return col_val; }
-
- bool operator == (const rowcol& pos) const
- { return *((long int *)this) == *((long int *)&pos); }
- // { return memcmp(this,&pos,sizeof(rowcol)); }
-
- // Offset the current position
- rowcol& operator += (const rowcol& pos)
- { row_val += pos.row_val; col_val += pos.col_val; return *this; }
- rowcol& operator -= (const rowcol& pos)
- { row_val -= pos.row_val; col_val -= pos.col_val; return *this; }
-
- friend inline rowcol operator + (const rowcol& pos1, const rowcol& pos2);
- friend inline rowcol operator - (const rowcol& pos1, const rowcol& pos2);
-
- // Scale the current position
- rowcol& operator *= (const int scalef)
- { row_val *= scalef; col_val *= scalef; return *this; }
-
- friend inline rowcol operator * (const rowcol& pos, const int scalef);
- friend inline rowcol operator << (const rowcol& pos, const int shiftf);
- friend inline rowcol operator >> (const rowcol& pos, const int shiftf);
- };
-
- inline rowcol operator + (const rowcol& pos1, const rowcol& pos2)
- { return rowcol(pos1.row_val+pos2.row_val, pos1.col_val+pos2.col_val);}
- inline rowcol operator - (const rowcol& pos1, const rowcol& pos2)
- { return rowcol(pos1.row_val-pos2.row_val, pos1.col_val-pos2.col_val);}
-
- inline rowcol operator * (const rowcol& pos, const int scalef)
- { return rowcol(pos.row_val*scalef, pos.col_val*scalef); }
- inline rowcol operator >> (const rowcol& pos, const int shiftf)
- { return rowcol(pos.row_val >> shiftf, pos.col_val >> shiftf); }
- inline rowcol operator << (const rowcol& pos, const int shiftf)
- { return rowcol(pos.row_val << shiftf, pos.col_val << shiftf); }
-
- // This is only a specification of a rectangle
- // area
- class rectarea
- {
- rowcol upperleft;
- rowcol lowright;
- public:
- // Specify the rect area by the coordinates of
- // the upper-left and lower-right corners
- rectarea(const rowcol _ul, const rowcol _lr) :
- upperleft(_ul), lowright(_lr) {}
-
- };
-
- class Extrema // Find min/max values of the (signed) image
- {
- GRAY_SIGNED max_value; // Max pixel value in the image
- GRAY_SIGNED min_value; // Min pixel value in the image
-
- rowcol max_pixel; // Position of the largest pixel
- rowcol min_pixel; // Position of the smallest pixel
-
- public:
- Extrema(const IMAGE& image); // Find extreme pixels of the image
-
- GRAY_SIGNED max(void) const { return max_value; }
- GRAY_SIGNED min(void) const { return min_value; }
-
- const rowcol& loc_max(void) { return max_pixel; }
- const rowcol& loc_min(void) { return min_pixel; }
- };
-
- /*
- *------------------------------------------------------------------------
- * Dealing with the rectangular area of the image
- */
-
- class Rectangle
- {
- friend class IMAGE;
-
- IMAGE& image; // The image I'm a rectangle of
- card nrows; // Dimension of the rectangle
- card ncols;
- GRAY * ptr; // Pointer to the upper left corner of
- // the rectangle
- int inc_to_nextrow; // inc_to_nextrow = p' - p =
- // = image.ncols - ncols
- // where
- // p = ptr + ncols points to the pixel
- // just beyond the current scanline of
- // the Rectangle
- // p' = (ptr - col) + image.ncols + col
- // points to the first pixel of the next
- // scanline of the rectangtle
- // col tells the column of the upper left
- // corner of the rectangle within the image
-
- // Private constructor
- // Note these are private constructors to
- // be used with IMAGE::square_of/rectangle
- // functions
- Rectangle
- (IMAGE& im, const card size, const rowcol pos);
- Rectangle
- (IMAGE& im, const rowcol uppleft, const rowcol lowright);
-
- public:
- // Note how to construct square area of
- // the image 'im':
- // im.square_of(10,rowcol(1,5))
- // and rectangular area
- // im.rectangle(rowcol(0,1),rowcol(4,5))
-
- Rectangle (IMAGE& im); // Treat entire image as a rectangle
- // Assign a value to all the pixels
- // in the rectangle area
- Rectangle& operator = (const int val);
- // Modify the pixels in the rectangle
- // area
- Rectangle& operator += (const int val);
- Rectangle& operator -= (const int val);
- Rectangle& operator *= (const int val);
- Rectangle& operator |= (const int val);
- Rectangle& operator &= (const int val);
- Rectangle& operator ^= (const int val);
- Rectangle& operator <<= (const int val);
- Rectangle& operator >>= (const int val);
-
- // Copy a rectangle a_rect into the
- // rectangular area of the image
- Rectangle& operator = (const Rectangle& a_rect);
-
- // Get a total sum of all the pixels
- friend double sum_over(const Rectangle& sa);
- };
-
- /*
- *------------------------------------------------------------------------
- * Some service procedures
- */
-
- inline
- int log2( // Find a binary logarithm of n
- const int n ) // and check that n is a power of two
- {
- register int i,k;
-
- assure(n > 0, "log2: the argument's got to be positive!");
-
- for(n & 0xff ? k=0,i=1 : k=8,i=256; i < n; k++, i*=2)
- ;
- if( i != n )
- _error("log2: the argument %d has got to be an exact power of two",n);
-
- return k;
- }
-
- inline
- int exp2(const int k) // Compute 2^k, k>=0
- {
- assure(k >= 0, "exp2: the argument may not be negative!");
- if( k > (signed)sizeof(int)*8-1 )
- _error("exp2: the exponent %d is too big",k);
-
- return 1<<k;
- }
-
- inline // return b * round( a/b )
- int round_to_even_multiple(const int a, const int b)
- {
- assert( b > 0 );
- register int rem = a % b;
- if( 2*rem < b )
- return a - rem;
- else
- return a + b - rem;
- }
-
- // Service functions (useful in the
- // verification code). They print some detail
- // info if the validation condition fails
- void verify_pixel_value(const IMAGE& im, const GRAY val);
-
- /*
- *------------------------------------------------------------------------
- * Inline Image Procedures
- */
-
- inline IMAGE::IMAGE(const card no_rows, const card no_cols, const card depth)
- {
- allocate(no_rows,no_cols,depth);
- }
-
- // Make a new image like the
- inline IMAGE::IMAGE(const IMAGE& old) // old one
- {
- allocate(old.nrows,old.ncols,old.bits_per_pixel);
- }
-
-
- inline IMAGE::IMAGE(const LazyImage& lazy_constructor)
- {
- allocate(lazy_constructor.nrows,lazy_constructor.ncols,
- lazy_constructor.depth);
- lazy_constructor.fill_in(*this);
- }
-
- inline GRAY& IMAGE::operator () (const card row, const card col)
- {
- is_valid();
- if( row >= nrows )
- _error("Row index %d is out of image boundaries [0,%d]",row,nrows-1);
- if( col >= ncols )
- _error("Col index %d is out of image boundaries [0,%d]",col,ncols-1);
-
- return (scanrows[row])[col];
- }
-
- inline const GRAY IMAGE::operator () (const card row, const card col) const
- { return (*(IMAGE *)this).operator()(row,col); }
-
- inline const GRAY IMAGE::operator () (const rowcol pos) const
- {
- return operator()(pos.row(),pos.col());
- }
-
- inline GRAY& IMAGE::operator () (const rowcol pos)
- {
- return operator()(pos.row(),pos.col());
- }
- // Make sure the pos is within the
- // image
- inline void IMAGE::sure_within(const rowcol pos) const
- {
- is_valid();
- if( pos.row() >= nrows )
- _error("Row index %d is out of image boundaries [0,%d]",pos.row(),nrows-1);
- if( pos.col() >= ncols )
- _error("Col index %d is out of image boundaries [0,%d]",pos.col(),ncols-1);
- }
-
- inline IMAGE& IMAGE::clear(void) // Clean the image
- {
- is_valid();
- memset(pixels,0,npixels*sizeof(GRAY));
- return *this;
- }
-
- inline IMAGE& IMAGE::invert(void) // Invert the image
- {
- is_valid();
- return (*this ^= ((1<<bits_per_pixel)-1));
- }
-
- inline void are_compatible(const IMAGE& im1, const IMAGE& im2)
- {
- im1.is_valid();
- im2.is_valid();
-
- if( im1.ncols != im2.ncols || im1.nrows != im2.nrows )
- _error("The image %dx%d and the image %dx%d have different sizes",
- im1.nrows,im1.ncols,im2.nrows,im2.ncols);
- }
-
-
- // Apply a user-defined action to each pixel
- inline IMAGE& IMAGE::apply(PixelPrimAction& action)
- {
- is_valid();
- for(register GRAY *pp=pixels; pp < pixels+npixels; pp++)
- action.operation(*pp);
-
- return *this;
- }
-
- // Construct a square rectangle
- inline Rectangle::Rectangle
- (IMAGE& im, const card sq_size, const rowcol pos)
- : image(im), nrows(sq_size), ncols(sq_size)
- {
- image.is_valid();
-
- if( pos.row() >= image.nrows ||
- pos.row() + nrows > image.nrows ||
- pos.col() >= image.ncols ||
- pos.col() + ncols > image.ncols )
- _error("Square area (left upper point [%d,%d], size %d)\n"
- "is not within the image %dx%d",
- pos.row(),pos.col(),sq_size,image.nrows,image.ncols);
-
- ptr = &(image.scanrows[pos.row()][pos.col()]);
- inc_to_nextrow = image.ncols - ncols;
- }
-
- // Clip a square area from the image
- inline Rectangle IMAGE::square_of(const card size, const rowcol pos)
- { return Rectangle(*this, size, pos); }
-
-
- // Make a rectangle with upper left corner
- // at uppleft position and lower right corner
- // at lowright position
- inline Rectangle::Rectangle
- (IMAGE& im, const rowcol uppleft, const rowcol lowright)
- : image(im), nrows(lowright.row()-uppleft.row()+1),
- ncols(lowright.col()-uppleft.col()+1)
- {
- image.is_valid();
-
- image.sure_within(uppleft);
- image.sure_within(lowright);
-
- ptr = &(image.scanrows[uppleft.row()][uppleft.col()]);
- inc_to_nextrow = image.ncols - ncols;
- }
-
-
- // Clip a rectangular area from the image
- inline Rectangle IMAGE::rectangle(const rowcol uppleft,const rowcol lowright)
- { return Rectangle(*this, uppleft, lowright); }
-
-
- // Treat the entire image as a rectangular area
- inline Rectangle::Rectangle (IMAGE& im)
- : image(im), nrows(im.nrows), ncols(im.ncols)
- {
- image.is_valid();
-
- ptr = image.pixels;
- inc_to_nextrow = 0;
- }
-
- #endif
-